1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.reflect;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.common.collect.Iterables.transform;
22
23 import com.google.common.annotations.VisibleForTesting;
24 import com.google.common.base.Function;
25 import com.google.common.base.Joiner;
26 import com.google.common.base.Objects;
27 import com.google.common.base.Predicates;
28 import com.google.common.collect.ImmutableList;
29 import com.google.common.collect.Iterables;
30
31 import java.io.Serializable;
32 import java.lang.reflect.AnnotatedElement;
33 import java.lang.reflect.Array;
34 import java.lang.reflect.GenericArrayType;
35 import java.lang.reflect.GenericDeclaration;
36 import java.lang.reflect.InvocationTargetException;
37 import java.lang.reflect.Method;
38 import java.lang.reflect.ParameterizedType;
39 import java.lang.reflect.Type;
40 import java.lang.reflect.TypeVariable;
41 import java.lang.reflect.WildcardType;
42 import java.util.Arrays;
43 import java.util.Collection;
44 import java.util.concurrent.atomic.AtomicReference;
45
46 import javax.annotation.Nullable;
47
48
49
50
51
52
53 final class Types {
54
55
56 private static final Function<Type, String> TYPE_NAME =
57 new Function<Type, String>() {
58 @Override public String apply(Type from) {
59 return JavaVersion.CURRENT.typeName(from);
60 }
61 };
62
63 private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null");
64
65
66 static Type newArrayType(Type componentType) {
67 if (componentType instanceof WildcardType) {
68 WildcardType wildcard = (WildcardType) componentType;
69 Type[] lowerBounds = wildcard.getLowerBounds();
70 checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds.");
71 if (lowerBounds.length == 1) {
72 return supertypeOf(newArrayType(lowerBounds[0]));
73 } else {
74 Type[] upperBounds = wildcard.getUpperBounds();
75 checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound.");
76 return subtypeOf(newArrayType(upperBounds[0]));
77 }
78 }
79 return JavaVersion.CURRENT.newArrayType(componentType);
80 }
81
82
83
84
85
86 static ParameterizedType newParameterizedTypeWithOwner(
87 @Nullable Type ownerType, Class<?> rawType, Type... arguments) {
88 if (ownerType == null) {
89 return newParameterizedType(rawType, arguments);
90 }
91
92 checkNotNull(arguments);
93 checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType);
94 return new ParameterizedTypeImpl(ownerType, rawType, arguments);
95 }
96
97
98
99
100
101 static ParameterizedType newParameterizedType(Class<?> rawType, Type... arguments) {
102 return new ParameterizedTypeImpl(
103 ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments);
104 }
105
106
107 private enum ClassOwnership {
108
109 OWNED_BY_ENCLOSING_CLASS {
110 @Nullable
111 @Override
112 Class<?> getOwnerType(Class<?> rawType) {
113 return rawType.getEnclosingClass();
114 }
115 },
116 LOCAL_CLASS_HAS_NO_OWNER {
117 @Nullable
118 @Override
119 Class<?> getOwnerType(Class<?> rawType) {
120 if (rawType.isLocalClass()) {
121 return null;
122 } else {
123 return rawType.getEnclosingClass();
124 }
125 }
126 };
127
128 @Nullable abstract Class<?> getOwnerType(Class<?> rawType);
129
130 static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior();
131
132 private static ClassOwnership detectJvmBehavior() {
133 class LocalClass<T> {}
134 Class<?> subclass = new LocalClass<String>() {}.getClass();
135 ParameterizedType parameterizedType = (ParameterizedType)
136 subclass.getGenericSuperclass();
137 for (ClassOwnership behavior : ClassOwnership.values()) {
138 if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) {
139 return behavior;
140 }
141 }
142 throw new AssertionError();
143 }
144 }
145
146
147
148
149
150 static <D extends GenericDeclaration> TypeVariable<D> newArtificialTypeVariable(
151 D declaration, String name, Type... bounds) {
152 return new TypeVariableImpl<D>(
153 declaration,
154 name,
155 (bounds.length == 0)
156 ? new Type[] { Object.class }
157 : bounds);
158 }
159
160
161 @VisibleForTesting static WildcardType subtypeOf(Type upperBound) {
162 return new WildcardTypeImpl(new Type[0], new Type[] { upperBound });
163 }
164
165
166 @VisibleForTesting static WildcardType supertypeOf(Type lowerBound) {
167 return new WildcardTypeImpl(new Type[] { lowerBound }, new Type[] { Object.class });
168 }
169
170
171
172
173
174
175
176
177
178
179 static String toString(Type type) {
180 return (type instanceof Class)
181 ? ((Class<?>) type).getName()
182 : type.toString();
183 }
184
185 @Nullable static Type getComponentType(Type type) {
186 checkNotNull(type);
187 final AtomicReference<Type> result = new AtomicReference<Type>();
188 new TypeVisitor() {
189 @Override void visitTypeVariable(TypeVariable<?> t) {
190 result.set(subtypeOfComponentType(t.getBounds()));
191 }
192 @Override void visitWildcardType(WildcardType t) {
193 result.set(subtypeOfComponentType(t.getUpperBounds()));
194 }
195 @Override void visitGenericArrayType(GenericArrayType t) {
196 result.set(t.getGenericComponentType());
197 }
198 @Override void visitClass(Class<?> t) {
199 result.set(t.getComponentType());
200 }
201 }.visit(type);
202 return result.get();
203 }
204
205
206
207
208
209 @Nullable private static Type subtypeOfComponentType(Type[] bounds) {
210 for (Type bound : bounds) {
211 Type componentType = getComponentType(bound);
212 if (componentType != null) {
213
214
215 if (componentType instanceof Class) {
216 Class<?> componentClass = (Class<?>) componentType;
217 if (componentClass.isPrimitive()) {
218 return componentClass;
219 }
220 }
221 return subtypeOf(componentType);
222 }
223 }
224 return null;
225 }
226
227 private static final class GenericArrayTypeImpl
228 implements GenericArrayType, Serializable {
229
230 private final Type componentType;
231
232 GenericArrayTypeImpl(Type componentType) {
233 this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType);
234 }
235
236 @Override public Type getGenericComponentType() {
237 return componentType;
238 }
239
240 @Override public String toString() {
241 return Types.toString(componentType) + "[]";
242 }
243
244 @Override public int hashCode() {
245 return componentType.hashCode();
246 }
247
248 @Override public boolean equals(Object obj) {
249 if (obj instanceof GenericArrayType) {
250 GenericArrayType that = (GenericArrayType) obj;
251 return Objects.equal(
252 getGenericComponentType(), that.getGenericComponentType());
253 }
254 return false;
255 }
256
257 private static final long serialVersionUID = 0;
258 }
259
260 private static final class ParameterizedTypeImpl
261 implements ParameterizedType, Serializable {
262
263 private final Type ownerType;
264 private final ImmutableList<Type> argumentsList;
265 private final Class<?> rawType;
266
267 ParameterizedTypeImpl(
268 @Nullable Type ownerType, Class<?> rawType, Type[] typeArguments) {
269 checkNotNull(rawType);
270 checkArgument(typeArguments.length == rawType.getTypeParameters().length);
271 disallowPrimitiveType(typeArguments, "type parameter");
272 this.ownerType = ownerType;
273 this.rawType = rawType;
274 this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments);
275 }
276
277 @Override public Type[] getActualTypeArguments() {
278 return toArray(argumentsList);
279 }
280
281 @Override public Type getRawType() {
282 return rawType;
283 }
284
285 @Override public Type getOwnerType() {
286 return ownerType;
287 }
288
289 @Override public String toString() {
290 StringBuilder builder = new StringBuilder();
291 if (ownerType != null) {
292 builder.append(JavaVersion.CURRENT.typeName(ownerType)).append('.');
293 }
294 builder.append(rawType.getName())
295 .append('<')
296 .append(COMMA_JOINER.join(transform(argumentsList, TYPE_NAME)))
297 .append('>');
298 return builder.toString();
299 }
300
301 @Override public int hashCode() {
302 return (ownerType == null ? 0 : ownerType.hashCode())
303 ^ argumentsList.hashCode() ^ rawType.hashCode();
304 }
305
306 @Override public boolean equals(Object other) {
307 if (!(other instanceof ParameterizedType)) {
308 return false;
309 }
310 ParameterizedType that = (ParameterizedType) other;
311 return getRawType().equals(that.getRawType())
312 && Objects.equal(getOwnerType(), that.getOwnerType())
313 && Arrays.equals(
314 getActualTypeArguments(), that.getActualTypeArguments());
315 }
316
317 private static final long serialVersionUID = 0;
318 }
319
320 private static final class TypeVariableImpl<D extends GenericDeclaration>
321 implements TypeVariable<D> {
322
323 private final D genericDeclaration;
324 private final String name;
325 private final ImmutableList<Type> bounds;
326
327 TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) {
328 disallowPrimitiveType(bounds, "bound for type variable");
329 this.genericDeclaration = checkNotNull(genericDeclaration);
330 this.name = checkNotNull(name);
331 this.bounds = ImmutableList.copyOf(bounds);
332 }
333
334 @Override public Type[] getBounds() {
335 return toArray(bounds);
336 }
337
338 @Override public D getGenericDeclaration() {
339 return genericDeclaration;
340 }
341
342 @Override public String getName() {
343 return name;
344 }
345
346 @Override public String toString() {
347 return name;
348 }
349
350 @Override public int hashCode() {
351 return genericDeclaration.hashCode() ^ name.hashCode();
352 }
353
354 @Override public boolean equals(Object obj) {
355 if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) {
356
357 if (obj instanceof TypeVariableImpl) {
358 TypeVariableImpl<?> that = (TypeVariableImpl<?>) obj;
359 return name.equals(that.getName())
360 && genericDeclaration.equals(that.getGenericDeclaration())
361 && bounds.equals(that.bounds);
362 }
363 return false;
364 } else {
365
366 if (obj instanceof TypeVariable) {
367 TypeVariable<?> that = (TypeVariable<?>) obj;
368 return name.equals(that.getName())
369 && genericDeclaration.equals(that.getGenericDeclaration());
370 }
371 return false;
372 }
373 }
374 }
375
376 static final class WildcardTypeImpl implements WildcardType, Serializable {
377
378 private final ImmutableList<Type> lowerBounds;
379 private final ImmutableList<Type> upperBounds;
380
381 WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
382 disallowPrimitiveType(lowerBounds, "lower bound for wildcard");
383 disallowPrimitiveType(upperBounds, "upper bound for wildcard");
384 this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds);
385 this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds);
386 }
387
388 @Override public Type[] getLowerBounds() {
389 return toArray(lowerBounds);
390 }
391
392 @Override public Type[] getUpperBounds() {
393 return toArray(upperBounds);
394 }
395
396 @Override public boolean equals(Object obj) {
397 if (obj instanceof WildcardType) {
398 WildcardType that = (WildcardType) obj;
399 return lowerBounds.equals(Arrays.asList(that.getLowerBounds()))
400 && upperBounds.equals(Arrays.asList(that.getUpperBounds()));
401 }
402 return false;
403 }
404
405 @Override public int hashCode() {
406 return lowerBounds.hashCode() ^ upperBounds.hashCode();
407 }
408
409 @Override public String toString() {
410 StringBuilder builder = new StringBuilder("?");
411 for (Type lowerBound : lowerBounds) {
412 builder.append(" super ").append(JavaVersion.CURRENT.typeName(lowerBound));
413 }
414 for (Type upperBound : filterUpperBounds(upperBounds)) {
415 builder.append(" extends ").append(JavaVersion.CURRENT.typeName(upperBound));
416 }
417 return builder.toString();
418 }
419
420 private static final long serialVersionUID = 0;
421 }
422
423 private static Type[] toArray(Collection<Type> types) {
424 return types.toArray(new Type[types.size()]);
425 }
426
427 private static Iterable<Type> filterUpperBounds(Iterable<Type> bounds) {
428 return Iterables.filter(
429 bounds, Predicates.not(Predicates.<Type>equalTo(Object.class)));
430 }
431
432 private static void disallowPrimitiveType(Type[] types, String usedAs) {
433 for (Type type : types) {
434 if (type instanceof Class) {
435 Class<?> cls = (Class<?>) type;
436 checkArgument(!cls.isPrimitive(),
437 "Primitive type '%s' used as %s", cls, usedAs);
438 }
439 }
440 }
441
442
443 static Class<?> getArrayClass(Class<?> componentType) {
444
445
446
447 return Array.newInstance(componentType, 0).getClass();
448 }
449
450
451 enum JavaVersion {
452
453 JAVA6 {
454 @Override GenericArrayType newArrayType(Type componentType) {
455 return new GenericArrayTypeImpl(componentType);
456 }
457 @Override Type usedInGenericType(Type type) {
458 checkNotNull(type);
459 if (type instanceof Class) {
460 Class<?> cls = (Class<?>) type;
461 if (cls.isArray()) {
462 return new GenericArrayTypeImpl(cls.getComponentType());
463 }
464 }
465 return type;
466 }
467 },
468 JAVA7 {
469 @Override Type newArrayType(Type componentType) {
470 if (componentType instanceof Class) {
471 return getArrayClass((Class<?>) componentType);
472 } else {
473 return new GenericArrayTypeImpl(componentType);
474 }
475 }
476 @Override Type usedInGenericType(Type type) {
477 return checkNotNull(type);
478 }
479 },
480 JAVA8 {
481 @Override Type newArrayType(Type componentType) {
482 return JAVA7.newArrayType(componentType);
483 }
484 @Override Type usedInGenericType(Type type) {
485 return JAVA7.usedInGenericType(type);
486 }
487 @Override String typeName(Type type) {
488 try {
489 Method getTypeName = Type.class.getMethod("getTypeName");
490 return (String) getTypeName.invoke(type);
491 } catch (NoSuchMethodException e) {
492 throw new AssertionError("Type.getTypeName should be available in Java 8");
493 } catch (InvocationTargetException e) {
494 throw new RuntimeException(e);
495 } catch (IllegalAccessException e) {
496 throw new RuntimeException(e);
497 }
498 }
499 }
500 ;
501
502 static final JavaVersion CURRENT;
503 static {
504 if (AnnotatedElement.class.isAssignableFrom(TypeVariable.class)) {
505 CURRENT = JAVA8;
506 } else if (new TypeCapture<int[]>() {}.capture() instanceof Class) {
507 CURRENT = JAVA7;
508 } else {
509 CURRENT = JAVA6;
510 }
511 }
512
513 abstract Type newArrayType(Type componentType);
514 abstract Type usedInGenericType(Type type);
515 String typeName(Type type) {
516 return Types.toString(type);
517 }
518
519 final ImmutableList<Type> usedInGenericType(Type[] types) {
520 ImmutableList.Builder<Type> builder = ImmutableList.builder();
521 for (Type type : types) {
522 builder.add(usedInGenericType(type));
523 }
524 return builder.build();
525 }
526 }
527
528
529
530
531
532
533
534
535
536
537
538 static final class NativeTypeVariableEquals<X> {
539 static final boolean NATIVE_TYPE_VARIABLE_ONLY =
540 !NativeTypeVariableEquals.class.getTypeParameters()[0].equals(
541 newArtificialTypeVariable(NativeTypeVariableEquals.class, "X"));
542 }
543
544 private Types() {}
545 }